/*
* Copyright(C) 2009, kesalin All rights reserved.
* 
* File name : UHostEventSink.h
* Brief     : HostEventSink.
* Version   : 1.0
* Author    : kesalin@gmail.com
* Date      : 2009-6-5
*/

#pragma once

#include "UCommon.h"

#include "UHostUIHandlerEx.h"

namespace LuoUtils {

// UHostEventTarget
class UHostEventTarget {

public:

	virtual void STDCALL OnNavigateComplete() = 0;

	virtual void STDCALL OnChangeTitle(LPCWSTR p) = 0;

}; // UHostEventTarget

// UHostEventSink
class UHostEventSink :
	public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>,
	public ATL::IDispEventSimpleImpl<1, UHostEventSink, &DIID_DWebBrowserEvents2> {

	UHostEventTarget* m_pTarget;

	ATL::CComPtr<IUnknown> m_Source;

	ATL::CComPtr<ITypeInfo> m_TypeInfo;

public:

	BEGIN_COM_MAP(UHostEventSink)
		COM_INTERFACE_ENTRY_IID(DIID_DWebBrowserEvents2, UHostEventSink)
	END_COM_MAP()

	BEGIN_SINK_MAP(UHostEventSink)
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, NavigateComplete2)
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE,  DocumentComplete)
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_ONQUIT,            OnQuit)
		SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_TITLECHANGE,       TitleChange)
	END_SINK_MAP()

public:

	/* */

	UHostEventSink() : m_pTarget(0)
	{
	}

	/* */

	virtual HRESULT GetFuncInfoFromId(const IID& iid, DISPID dispidMember, LCID lcid, ATL::_ATL_FUNC_INFO& info)
	{
		if (m_TypeInfo != 0) {
			return ATL::AtlGetFuncInfoFromId(m_TypeInfo, iid, dispidMember, lcid, info);
		}

		return E_NOTIMPL;
	}

	/* */

	static HRESULT Create(UHostEventSink** pp, UHostEventTarget* p)
	{
		if (pp == 0) {
			return E_INVALIDARG;
		}

		*pp = 0;

		ATL::CComObject<UHostEventSink>* obj = 0;
		HRESULT hRslt = ATL::CComObject<UHostEventSink>::CreateInstance(&obj);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		obj->AddRef();

		*pp = static_cast<UHostEventSink*>(obj);

		obj->m_pTarget = p;

		return S_OK;
	}

	/* */

	HRESULT DoAdvise(IUnknown* pUnk)
	{ 
		m_Source = pUnk;

		ATL::CComQIPtr<IDispatch> disp(pUnk);
		if (disp != 0) {
			UINT cti = 0;
			disp->GetTypeInfoCount(&cti);
			if (cti > 0) {
				ATL::CComPtr<ITypeInfo> ti;
				HRESULT hRslt = disp->GetTypeInfo(
					0,
					0,
					&ti);
				if (SUCCEEDED(hRslt)) {
					ATL::CComPtr<ITypeLib> tlib;
					UINT idx = 0;
					hRslt = ti->GetContainingTypeLib(
						&tlib,
						&idx);
					if (SUCCEEDED(hRslt)) {
						ATL::CComPtr<ITypeInfo> ti2;
						hRslt = tlib->GetTypeInfoOfGuid(
							DIID_DWebBrowserEvents2,
							&ti2);
						if (SUCCEEDED(hRslt)) {
							m_TypeInfo = ti2;
						}
					}
				}
			}
		}

		return DispEventAdvise(m_Source, &DIID_DWebBrowserEvents2);
	}

	HRESULT DoUnadvise()
	{
		if (m_Source == 0) {
			return S_FALSE;
		}

		HRESULT hRslt = DispEventUnadvise(m_Source, &DIID_DWebBrowserEvents2);

		m_Source.Release();

		if (FAILED(hRslt)) {
			return hRslt;
		}

		return S_OK;
	}

	/* */

	STDMETHOD_(void, NavigateComplete2)(
		IDispatch* pDisp,
		VARIANT*   URL)
	{
		ATLTRACE("UHostEventSink::NavigateComplete2()\n");

		if (m_pTarget != 0) {
			m_pTarget->OnNavigateComplete();
		}
	}

	STDMETHOD_(void, OnQuit)()
	{
		DoUnadvise();
	}

	/* */

	STDMETHOD_(void, DocumentComplete)(
		IDispatch* pDisp,
		VARIANT*   URL)
	{
		ATLTRACE("UHostEventSink::DocumentComplete()\n");

		ATL::CComQIPtr<IWebBrowser2> web(m_Source);
		if (web != 0) {
			ATL::CComPtr<IDispatch> doc;
			web->get_Document(&doc);
			if (doc != 0) {
				ATL::CComPtr<UHostUIHandlerEx> ex;
				UHostUIHandlerEx::Create(&ex);

				if (ex != 0) {
					ATL::CComQIPtr<IHTMLDocument2> hdoc(doc);
					if (hdoc != 0) {
						ATL::CComQIPtr<IOleObject> ole(doc);
						if (ole != 0) {
							ATL::CComPtr<IOleClientSite> site;
							ole->GetClientSite(&site);
							if (site != 0) {
								ATL::CComQIPtr<IDocHostUIHandler> ui(site);
								ATL::CComQIPtr<IOleCommandTarget> ct(site);
								if (ui != 0 && ct != 0) {
									ex->SetupDefault(ui, ct);
								} else {
									ex.Release();
								}
							}
						}
					}
				}

				if (ex != 0) {
					ATL::CComQIPtr<ICustomDoc> cdoc(doc);
					if (cdoc != 0) {
						cdoc->SetUIHandler(ex);
					}
				}
			}
		}
	}

	/* */

	STDMETHOD_(void, TitleChange)(
		BSTR bstr)
	{
		if (bstr != 0 && m_pTarget != 0) {
			m_pTarget->OnChangeTitle(bstr);
		}
	}

	/* */

}; // UHostEventSink

} // namespace LuoUtils

